package com.hsyt.pfc.support.util;

import com.alibaba.fastjson.JSON;
import okhttp3.*;
import okio.Buffer;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.log4j.Logger;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.io.ByteArrayInputStream;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.lang.String.valueOf;

/**
* @Author: wangze@hsyuntai.com on 2017/6/8.
 */
public class ClientUtil {
    private final static Logger logger = Logger.getLogger("logfile");

    private final static long CONNECT_TIMEOUT = 30L; //设置连接超时时间
    private final static long READ_TIMEOUT = 45L; //设置读取超时时间
    private final static long WRITE_TIMEOUT = 20L; //设置写的超时时间

    public static final MediaType CONTENT_JSON = MediaType.parse("application/json; charset=utf-8");
    public static final MediaType CONTENT_TEXT = MediaType.parse("application/x-www-form-urlencoded; charset=utf-8");
    public static final MediaType CONTENT_FORM = MediaType.parse("multipart/form-data; charset=utf-8");

    public static final String ACCEPT_JSON = "application/json";
    public static final String ACCEPT_XML = "text/xml";

    private static OkHttpClient.Builder client = new OkHttpClient().newBuilder()
            .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
            .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
            .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS);

    public static Request init(String url, String data, MediaType mediaType, String accept, byte[] content, String fileKey, String fileName) {
        try {
            Request request = null;
            if (null == content) {
                request = new Request.Builder().url(url).post(RequestBody.create(null == mediaType ? CONTENT_JSON : mediaType, data)).addHeader("Accept", StringUtils.isBlank(accept) ? ACCEPT_JSON : accept).addHeader("Connection", "close").build();
            } else {
                MultipartBody.Builder requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM);
                RequestBody body = RequestBody.create(MediaType.parse("image/*"), content);// MediaType.parse() 里面是上传的文件类型。
                requestBody.addFormDataPart(StringUtil.isEmpty(fileKey) ? "image_content" : fileKey, StringUtil.isEmpty(fileName) ? "default.jpg" : fileName, body); // 参数分别为,请求key,文件名称,RequestBody
                try { //URL参数处理
                    if (StringUtil.isNotEmpty(data)) {
                        Map<String, String> req = new HashMap<>();
                        if (CONTENT_FORM.equals(mediaType) || CONTENT_TEXT.equals(mediaType)) {
                            String arr[] = data.split("&");
                            for (String key_value : arr) {
                                Matcher matcher = Pattern.compile("=").matcher(key_value);
                                if (matcher.find()) {
                                    int firstAppearIndex = matcher.start();
                                    String key = key_value.substring(0, firstAppearIndex);
                                    String value = key_value.substring(firstAppearIndex + 1, key_value.length());
                                    requestBody.addFormDataPart(key, value);
                                }
                            }
                        } else {
                            Map<String, String> map = JSON.parseObject(data, Map.class);
                            Set<Map.Entry<String, String>> entries = map.entrySet();
                            for (Map.Entry entry : entries) {
                                String key = valueOf(entry.getKey());
                                String value = valueOf(entry.getValue());
                                requestBody.addFormDataPart(key, value);
                            }
                        }
                    }
                } catch (Exception e) {
                    logger.error("URL参数处理异常,请检查对应参数格式是否正确" + e);
                    throw new RuntimeException("URL参数处理异常,请检查对应参数格式是否正确" + e);
                }
                request = new Request.Builder().url(url).post(requestBody.build()).addHeader("Accept", StringUtils.isBlank(accept) ? ACCEPT_JSON : accept).addHeader("Connection", "close").build();

            }
            if (request.isHttps()) {
                client.sslSocketFactory(SSLContexts.custom().loadTrustMaterial(null, new TrustStrategy() {
                    @Override
                    public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                        return true;
                    }
                }).build().getSocketFactory());
                client.hostnameVerifier(new HostnameVerifier() {
                    @Override
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                });
            }
            return request;
        } catch (Exception exception) {
            throw new RuntimeException(exception.getMessage());
        }
    }

    public static String post(Request request, String module) {
        long thread = Thread.currentThread().getId();
        Long timestamp = System.currentTimeMillis();
        Buffer buffer = null;
        String result = "";
        Response response = null;
        try {
            buffer = new Buffer();
            logger.info("****************************************TCP连接日志 - 启动 - [" + (StringUtil.isBlank(module) ? "client" : module) + "] ****************************************");
            logger.info("THREAD_ID:[" + thread + "]      >>>      TCP:URL:              >>>     " + request.url());
//            logger.info("THREAD_ID:[" + thread + "]      >>>      TCP:CONTENT:          >>>     " + request.body().contentType());
//            logger.info("THREAD_ID:[" + thread + "]      >>>      TCP:HEADER:           >>>     " + request.header("Accept"));
            request.body().writeTo(buffer);
            logger.info("THREAD_ID:[" + thread + "]      >>>      TCP:SEND              >>>     " + (StringUtil.isNotBlank(module) ? (StringUtil.contains(module.toLowerCase(), "image") ? "当前client含图片内容,send不打印记录" : buffer.readUtf8()) : buffer.readUtf8()));
            response = client.build().newCall(request).execute();
            logger.info("THREAD_ID:[" + thread + "]      >>>      TCP:COST              >>>     " + String.valueOf(System.currentTimeMillis() - timestamp) + "ms");
            if (!response.isSuccessful()) {
                throw new Exception("Unexpected code " + response);
            }
            result = response.body().string();
            logger.info("THREAD_ID:[" + thread + "]      >>>      TCP:ACCEPT            >>>     " + result.replaceAll("\r|\n", ""));
        } catch (Exception exception) {
            logger.error("THREAD_ID:[" + thread + "]      >>>      TCP:EXCEPTION         >>>     " + exception.getMessage());
            throw new RuntimeException(exception.getMessage());
        } finally {
            logger.info("****************************************TCP连接日志 - 结束 - [" + (StringUtil.isBlank(module) ? "client" : module) + "] ****************************************");
            try {
                if (null != buffer) {
                    buffer.close();
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }
        return result;
    }

    public static void secure(byte[] certificate, String password) {
        try {
            if (StringUtil.isBlank(password) || null == certificate || certificate.length <= 0) {
                return;
            }
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            keyStore.load(new ByteArrayInputStream(certificate), password.toCharArray()); //设置证书密码
            // trust own CA and all self-signed certs
            client.sslSocketFactory(SSLContexts.custom().loadKeyMaterial(keyStore, password.toCharArray()).build().getSocketFactory());
        } catch (Exception exception) {
            logger.error("TCP证书加载失败", exception);
            throw new RuntimeException(exception.getMessage());
        }
    }

    /* 以上方法不许随意修改 */

    public static String post(Atom atom) {
        Request request = init(atom.url, atom.data, atom.content, atom.accept, null, null, null);
        secure(atom.certificate, atom.password);
        return post(request, atom.module);
    }

    /**
     * @return
     * @Description 针对表单上传单个文件使用[后续可适当扩展为多文件上传]
     */
    public static String post(Atom atom, byte[] content, String fileKey, String fileName) {
        Request request = init(atom.url, atom.data, atom.content, atom.accept, content, fileKey, fileName);
        secure(atom.certificate, atom.password);
        return post(request, atom.module);
    }

    public static class Atom {
        private String url;
        private String data;
        private MediaType content;
        private String accept;
        private byte[] certificate;
        private String password;
        private String module;

        public Atom(String url, String data, String module) {
            this.url = url;
            this.data = data;
            this.module = module;
        }

        public Atom url(String url) {
            this.url = url;
            return this;
        }

        public Atom data(String data) {
            this.data = data;
            return this;
        }

        public MediaType content() {
            return content;
        }

        public Atom content(MediaType content) {
            this.content = content;
            return this;
        }

        public Atom accept(String accept) {
            this.accept = accept;
            return this;
        }

        public Atom certificate(byte[] certificate) {
            this.certificate = certificate;
            return this;
        }

        public Atom password(String password) {
            this.password = password;
            return this;
        }

        public Atom module(String module) {
            this.module = module;
            return this;
        }
    }


    public static void main(String[] args) {
//        String request = "{\"token\":\"8g5gzPxSkKrxcy0qbsFT\",\"systemId\":\"YT000001\",\"time\":\"1499397502071\",\"sign\":\"8145c92b5f216da6f040ba2cbe668f27\",\"param\":\"WAwUfJATMXo6O9vP2CI6OSvmcFAr4E1J8Z5MFcvb+3+FheDMDhwQ9v7aamj3244GwQ7hwEKp3jz6IIfPkv4NENwjSc0vvdzEgn9zV10rXJ3eGKCG7YdUXn1OM9Nqh0seTSFT/V9Rpy4Bp0KtnrC1KO4yAbr7RfHRpui21RYZotI9lNOJgrMvmhKWBPbu9vSDb9q1hwCn1FWpHLR3zJ6rKcUBquyV8h791h7LvyckBIu/ymZw4s04EUzDIaBgXCMVoVTW3S4Fth/zC7gVhRM1uRK7/VVvS0pcdGoByZWT7+0iX8iQUYSiWNUJRKvn9s65o8//eyB3WnWe9rRERhFQCO3wa9A1b4pZkQrFIuwQ7UfE6LatA7/o9qkhIRTXcfkYzQkoKWNvaCw=\"}";
//        String response =  HttpUtil.post(new Atom("https://210.36.48.34:8080/ektService/ektTrans/accountDepositPurchase.json", request,"test").content(HttpUtil.CONTENT_JSON).accept(HttpUtil.ACCEPT_JSON));
//        String request = "HospCode=F000106&InputXml=<?xml version=\"1.0\" encoding=\"utf-8\" ?><data><OrderNo>YT_02017072403000000000060871736</OrderNo></data>&RspType=XML";
//        String request = "{\"actualTrade\":\"0.01\",\"bizId\":\"65\",\"expandData\":\"3009720|18758279575\",\"inPaymentNo\":\"2017081121001004120297421432\",\"invokeCount\":\"1\",\"operateType\":\"1\",\"outPaymentNo\":\"YT_02017081110400000000258757002\",\"paymentTime\":\"2017-08-11 09:17:59\",\"sign\":\"MC0CFAdqMu5GtUHocy4B7Z1hLBi0iyP9AhUAqMzUtZSgxWHLBSu6gannG+cu0Z8=\",\"tradeChannel\":\"2\",\"tradeStatus\":\"2\"}";
//        String response = HttpUtil.post(new Atom("http://115.29.163.148:7582/yt-med-restapi/r/60360/131", request, "test").content(HttpUtil.CONTENT_JSON));
//        logger.info(response);

    }

}
